package com.admin_system.config;

import com.admin_system.utils.JwtTokenUtil;
import com.admin_system.service.UserService;
import com.admin_system.pojo.User;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.MalformedJwtException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Collections;
import java.util.Date;

/**
 * JWT请求过滤器
 * 用于拦截所有HTTP请求，检查JWT令牌的有效性
 * 实现了基于令牌的身份验证和授权机制
 * 继承OncePerRequestFilter确保每个请求只会被过滤一次
 */
@Component
public class JwtRequestFilter extends OncePerRequestFilter {

    /** 日志记录器 */
    private static final Logger logger = LoggerFactory.getLogger(JwtRequestFilter.class);

    /** 用户服务，用于验证令牌的有效性 */
    @Autowired
    @Lazy  // 延迟加载，避免循环依赖
    private UserService userService;

    /** JWT令牌工具，用于解析和验证令牌 */
    @Autowired
    private JwtTokenUtil jwtTokenUtil;

    /**
     * 过滤器内部处理逻辑
     * 拦截所有请求，提取并验证JWT令牌，设置安全上下文
     *
     * @param request HTTP请求对象
     * @param response HTTP响应对象
     * @param chain 过滤器链，用于传递请求到下一个过滤器
     * @throws ServletException 如果处理过程中发生Servlet异常
     * @throws IOException 如果处理过程中发生IO异常
     */
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
            throws ServletException, IOException {

        // 从请求头中获取Authorization值
        final String requestTokenHeader = request.getHeader("Authorization");
        
        // 获取请求路径
        String path = request.getRequestURI();
        
        // 检查是否为白名单URL（不需要登录的URL）
        if (isWhitelistedPath(path)) {
            chain.doFilter(request, response);
            return;
        }

        // 检查是否请求HTML资源，如果是，则允许通过，交给SecurityFilterChain处理
        if (path.endsWith(".html")) {
            chain.doFilter(request, response);
            return;
        }

        // 用于存储从令牌中提取的用户名和JWT令牌
        String username = null;
        String jwtToken = null;

        // 获取JWT令牌（去掉Bearer前缀）
        if (requestTokenHeader != null && requestTokenHeader.startsWith("Bearer ")) {
            jwtToken = requestTokenHeader.substring(7);
            try {
                // 从令牌中提取用户名
                username = jwtTokenUtil.getUsernameFromToken(jwtToken);
            } catch (ExpiredJwtException e) {
                // 处理令牌过期异常
                logger.warn("JWT令牌已过期: {}", e.getMessage());
                // 对于API请求返回401未授权状态码
                if (isApiRequest(request)) {
                    response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
                    response.getWriter().write("{\"error\":\"token_expired\",\"message\":\"会话已过期，请重新登录\"}");
                    return;
                }
                // 对于页面请求重定向到登录页，并带上过期错误信息
                else {
                    response.sendRedirect("/login.html?error=expired");
                    return;
                }
            } catch (MalformedJwtException e) {
                // 处理格式错误的令牌
                logger.warn("无效的JWT令牌: {}", e.getMessage());
                if (isApiRequest(request)) {
                    response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
                    response.getWriter().write("{\"error\":\"invalid_token\",\"message\":\"无效的令牌\"}");
                    return;
                } else {
                    response.sendRedirect("/login.html?error=invalid");
                    return;
                }
            } catch (Exception e) {
                // 处理其他令牌解析错误
                logger.error("解析JWT令牌发生错误: {}", e.getMessage(), e);
                if (isApiRequest(request)) {
                    response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
                    response.getWriter().write("{\"error\":\"token_error\",\"message\":\"令牌验证错误\"}");
                    return;
                } else {
                    response.sendRedirect("/login.html?error=invalid");
                    return;
                }
            }
        } else {
            // 处理没有提供令牌的情况
            if (!isPublicPath(path)) {
                logger.warn("JWT令牌未提供");
                // 对于API请求返回401未授权状态码
                if (isApiRequest(request)) {
                    response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
                    response.getWriter().write("{\"error\":\"missing_token\",\"message\":\"请先登录\"}");
                    return;
                }
                // 对于页面请求，允许继续，交给Security处理重定向
                else {
                    chain.doFilter(request, response);
                    return;
                }
            }
        }

        // 验证令牌并设置安全上下文
        if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
            try {
                // 检查令牌是否为最新的令牌（防止多地点登录）
                boolean isValidToken = userService.validateUserToken(username, jwtToken);
                
                if (!isValidToken) {
                    User user = userService.findByUsername(username);
                    boolean isTokenExpired = false;
                    
                    // 检查token是否过期
                    try {
                        isTokenExpired = jwtTokenUtil.isTokenExpired(jwtToken);
                    } catch (ExpiredJwtException e) {
                        isTokenExpired = true;
                    }
                    
                    // 如果token已过期，返回会话过期信息
                    if (isTokenExpired) {
                        logger.warn("用户 {} 的令牌已过期", username);
                        if (isApiRequest(request)) {
                            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
                            response.getWriter().write("{\"error\":\"token_expired\",\"message\":\"会话已过期，请重新登录\"}");
                            return;
                        } else {
                            response.sendRedirect("/login.html?error=expired");
                            return;
                        }
                    } 
                    // 如果token与数据库中最新token不匹配，则说明在其他地方登录
                    else if (user != null && user.getLastToken() != null && !user.getLastToken().equals(jwtToken)) {
                        // 先检查是否是同一用户在同一浏览器重新获取了token
                        // 如果请求来自同一用户的同一浏览器但token不匹配，更可能是会话刷新问题
                        String referer = request.getHeader("Referer");
                        String userAgent = request.getHeader("User-Agent");
                        logger.info("用户: {}, 请求头: {}, User-Agent: {}", username, referer, userAgent);
                        
                        // 判断token是否只是自动刷新导致的不匹配，而非其他地方登录
                        boolean isAutoRefresh = false;
                        try {
                            // 检查token创建时间，如果是近期创建的token，可能是自动刷新导致
                            Date tokenIssuedAt = jwtTokenUtil.getIssuedDateFromToken(jwtToken);
                            long tokenAge = System.currentTimeMillis() - tokenIssuedAt.getTime();
                            
                            // 如果token创建时间在10分钟内，且用户最近有登录活动，视为自动刷新
                            if (tokenAge < 10 * 60 * 1000 && user.getLastLoginTime() != null) {
                                long timeSinceLastLogin = System.currentTimeMillis() - user.getLastLoginTime().getTime();
                                // 如果最近一小时内有登录活动
                                if (timeSinceLastLogin < 60 * 60 * 1000) {
                                    isAutoRefresh = true;
                                }
                            }
                        } catch (Exception e) {
                            logger.warn("检查token刷新状态出错: {}", e.getMessage());
                        }
                        
                        if (isAutoRefresh) {
                            logger.info("检测到token自动刷新: {}", username);
                            if (isApiRequest(request)) {
                                response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
                                response.getWriter().write("{\"error\":\"token_refreshed\",\"message\":\"会话已刷新，请使用新的会话\"}");
                                return;
                            } else {
                                response.sendRedirect("/login.html?error=session_refreshed");
                                return;
                            }
                        } else {
                            logger.warn("令牌已被其他地方的登录操作使令牌失效");
                            if (isApiRequest(request)) {
                                response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
                                response.getWriter().write("{\"error\":\"token_invalidated\",\"message\":\"您的账号已在其他地方登录，请重新登录\"}");
                                return;
                            } else {
                                response.sendRedirect("/login.html?error=elsewhere");
                                return;
                            }
                        }
                    }
                    // 其他原因导致的验证失败
                    else {
                        logger.warn("令牌验证失败");
                        if (isApiRequest(request)) {
                            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
                            response.getWriter().write("{\"error\":\"authentication_failed\",\"message\":\"请重新登录\"}");
                            return;
                        } else {
                            response.sendRedirect("/login.html?error=auth_failed");
                            return;
                        }
                    }
                }
                
                // 从令牌中获取用户角色
                String role = jwtTokenUtil.getRoleFromToken(jwtToken);
                
                // 创建认证令牌并设置用户权限
                UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(
                        username, null, Collections.singletonList(new SimpleGrantedAuthority(role)));
                
                // 设置认证详情（如IP地址、会话ID等）
                authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                
                // 设置安全上下文，表示用户已通过身份验证
                SecurityContextHolder.getContext().setAuthentication(authenticationToken);
            } catch (Exception e) {
                logger.error("令牌验证过程中发生错误", e);
                if (isApiRequest(request)) {
                    response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
                    response.getWriter().write("{\"error\":\"authentication_error\",\"message\":\"认证过程中发生错误，请重新登录\"}");
                    return;
                } else {
                    response.sendRedirect("/login.html?error=auth_error");
                    return;
                }
            }
        }
        
        // 继续过滤器链的处理
        chain.doFilter(request, response);
    }
    
    /**
     * 检查是否是公共访问路径（不需要令牌的路径）
     * 包括白名单路径和以/api/public/开头的公共API
     * 
     * @param path 请求路径
     * @return 如果是公共路径返回true，否则返回false
     */
    private boolean isPublicPath(String path) {
        return isWhitelistedPath(path) || 
               path.startsWith("/api/public/") || 
               path.startsWith("/api/forgot-password/") || 
               path.startsWith("/api/materials/public/");
    }
    
    /**
     * 检查是否是白名单路径
     * 包括登录、注册页面和静态资源等
     * 
     * @param path 请求路径
     * @return 如果是白名单路径返回true，否则返回false
     */
    private boolean isWhitelistedPath(String path) {
        return path.equals("/login") || 
               path.equals("/register") ||
               path.equals("/forgot-password.html") ||
               path.startsWith("/assets/") ||
               path.endsWith(".html") || 
               path.endsWith(".js") || 
               path.endsWith(".css") || 
               path.endsWith(".ico") || 
               path.endsWith(".png") || 
               path.endsWith(".jpg") || 
               path.endsWith(".gif") || 
               path.endsWith(".svg");
    }
    
    /**
     * 判断是否为API请求
     * 通过请求路径和请求头判断是否是API调用
     * 
     * @param request HTTP请求对象
     * @return 如果是API请求返回true，否则返回false
     */
    private boolean isApiRequest(HttpServletRequest request) {
        String path = request.getRequestURI();
        return path.startsWith("/api/") || 
               path.equals("/downloadExcel") ||
               path.equals("/processExcel") ||
               request.getHeader("Accept") != null && request.getHeader("Accept").contains("application/json") ||
               request.getHeader("Content-Type") != null && (
                   request.getHeader("Content-Type").contains("application/json") || 
                   request.getHeader("Content-Type").contains("multipart/form-data")
               );
    }
} 